//MIT License
//
//Copyright(c) 2020 Goodman Tao
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files(the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions :
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#include "../__include/_LB_1_MemoryManager.h"

#include "GlobalVars.h"


GlobalVars* pVars;

typedef void (*TestFunc)();
typedef struct
{
	const char* Name;
	TestFunc func;
} Test;

/////////////////////////////////////////////////////////////

/* test_first */
void test_first()
{
	printf("first tested.\n");
}

/* test_thread_arg_and_retval */
static int thread_test_args(void* aArg)
{
	printf("thread: %d.\n", *(int*)aArg);
	return *(int*)aArg;
}

#define TEST_THREAD_ARGS_N_THREADS 4

static void test_thread_arg_and_retval(void)
{
	__MEMS();

	Thread * threads[TEST_THREAD_ARGS_N_THREADS];
	for (size_t i = 0; i < TEST_THREAD_ARGS_N_THREADS; i++)
	{
		threads[i] = __SNEW(Thread);
	}
	int ids[TEST_THREAD_ARGS_N_THREADS];
	int retval;

	for (int i = 0; i < TEST_THREAD_ARGS_N_THREADS; i++)
	{
		ids[i] = rand();
		threads[i]->Call(threads[i], "Create", thread_test_args, (void*)&ids[i], NULL);
	}

	for (int i = 0; i < TEST_THREAD_ARGS_N_THREADS; i++)
	{
		threads[i]->Call(threads[i], "Join", &retval, NULL);
		if (retval != ids[i])
		{
			printf("Error occured..\n");
		}
	}

	__MEMT();
}


/* test_thread_local_storage */
static int thread_test_local_storage(void* aArg)
{
	printf("set thread local var.\n");
	__STATIC(GlobalVars)->SetThreadLocalVar(rand());
	return 0;
}

static void test_thread_local_storage(void)
{
	__MEMS();

	Thread* pTh = __SNEW(Thread);
	__STATIC(GlobalVars)->SetThreadLocalVar(1);
	pTh->Call(pTh, "Create", thread_test_local_storage, NULL, NULL);
	pTh->Call(pTh, "Join", NULL, NULL);

	printf("threadLocalVar: %d.\n", __STATIC(GlobalVars)->GetThreadLocalVar());

	__MEMT();
}


/* test_mutex_locking */
#define TEST_THREAD_LOCK_ITERATIONS_PER_THREAD 10000

static int thread_lock(void* aArg)
{
	__MEMS();

	Mutex* pGlblMtx = NULL;
	pVars->Call(pVars, "GetMutex", &pGlblMtx);

	for (int i = 0; i < TEST_THREAD_LOCK_ITERATIONS_PER_THREAD; ++i)
	{
		pGlblMtx->Call(pGlblMtx, "Lock", NULL);

		ConcurrenceReturnValue ret = ConcurrenceReturnValue_NON;
		pGlblMtx->Call(pGlblMtx, "TryLock", &ret);
		if (ret != ConcurrenceReturnValue_BUSY) { int a = 0; int b = 1 / a; }

		pVars->Call(pVars, "++");
		
		pGlblMtx->Call(pGlblMtx, "Unlock", NULL);
	}


	Mutex* pTryMtx = __SNEW(Mutex);
	pTryMtx->Call(pTryMtx, "Init", MutexType_PLAIN, NULL);

	pGlblMtx->Call(pGlblMtx, "Lock", NULL);
	for (int i = 0; i < TEST_THREAD_LOCK_ITERATIONS_PER_THREAD; ++i)
	{
		ConcurrenceReturnValue ret = ConcurrenceReturnValue_NON;
		pTryMtx->Call(pTryMtx, "TryLock", &ret);
		if (ret != ConcurrenceReturnValue_SUCCESS) { int a = 0; int b = 1 / a; }
		pTryMtx->Call(pTryMtx, "TryLock", &ret);
		if (ret != ConcurrenceReturnValue_BUSY) { int a = 0; int b = 1 / a; }

		pVars->Call(pVars, "++");
		
		pTryMtx->Call(pTryMtx, "Unlock", NULL);
	}
	pGlblMtx->Call(pGlblMtx, "Unlock", NULL);

	__MEMT();
	return 0;
}

#define TEST_MUTEX_LOCKING_N_THREADS 128

static void test_mutex_locking(void)
{
	__MEMS();

	Thread * t[TEST_MUTEX_LOCKING_N_THREADS];

	pVars->Call(pVars, "SetCount", 0);

	for (int i = 0; i < TEST_MUTEX_LOCKING_N_THREADS; ++i)
	{
		t[i] = __SNEW(Thread);
		t[i]->Call(t[i], "Create", thread_lock, NULL, NULL);
	}

	for (int i = 0; i < TEST_MUTEX_LOCKING_N_THREADS; ++i)
	{
		t[i]->Call(t[i], "Join", NULL, NULL);
	}

	int tmp = 0;
	printf(" equal ? %d.\n", ((pVars->Call(pVars, "GetCount", &tmp), tmp) == (TEST_MUTEX_LOCKING_N_THREADS * TEST_THREAD_LOCK_ITERATIONS_PER_THREAD * 2)));

	__MEMT();
}

/* test_mutex_recursive */
struct TestMutexData {
	Mutex* mtx;
	volatile int i;
	volatile int completed;
};

static int test_mutex_recursive_cb(void* data)
{
	const int iterations = 10000;
	struct TestMutexData* mutex_data = data;

	ConcurrenceReturnValue ret = ConcurrenceReturnValue_NON;
	mutex_data->mtx->Call(mutex_data->mtx, "Lock", &ret);
	if (ret != ConcurrenceReturnValue_SUCCESS) { int a = 0; int b = 1 / a; }

	for (int i = 0; i < iterations; i++)
	{
		mutex_data->mtx->Call(mutex_data->mtx, "Lock", NULL);
		if (mutex_data->i != i) { int a = 0; int b = 1 / a; }
		mutex_data->i += 1;
	}

	for (int i = iterations - 1; i >= 0; i--)
	{
		mutex_data->mtx->Call(mutex_data->mtx, "Unlock", NULL);
		mutex_data->i -= 1;
		if (mutex_data->i != i) { int a = 0; int b = 1 / a; }
	}

	if (mutex_data->i != 0) { int a = 0; int b = 1 / a; }

	mutex_data->completed++;

	mutex_data->mtx->Call(mutex_data->mtx, "Unlock", NULL);

	return 0;
}

#define TEST_MUTEX_RECURSIVE_N_THREADS 128

static void test_mutex_recursive(void)
{
	__MEMS();

	Thread* t[TEST_MUTEX_RECURSIVE_N_THREADS];
	struct TestMutexData data;

	data.mtx = __SNEW(Mutex);
	data.mtx->Call(data.mtx, "Init", MutexType_RECURSIVE, NULL);
	data.i = 0;
	data.completed = 0;

	for (int i = 0; i < TEST_MUTEX_RECURSIVE_N_THREADS; i++)
	{
		t[i] = __SNEW(Thread);
		t[i]->Call(t[i], "Create", test_mutex_recursive_cb, &data, NULL);
	}

	for (int i = 0; i < TEST_MUTEX_RECURSIVE_N_THREADS; i++)
	{
		t[i]->Call(t[i], "Join", NULL, NULL);
	}

	if (data.completed != TEST_MUTEX_RECURSIVE_N_THREADS) { int a = 0; int b = 1 / a; }

	__MEMT();
}

/* test_condition_variables */
static int thread_condition_notifier(void* aArg)
{
	(void)aArg;

	Mutex* pMtx = NULL;
	pVars->Call(pVars, "GetMutex", &pMtx);
	ConditionVariable* pCndVar = NULL;
	pVars->Call(pVars, "GetCondVar", &pCndVar);

	pMtx->Call(pMtx, "Lock", NULL);
	printf("calling --.....................................\n");
	pVars->Call(pVars, "--");
	int count = 0;
	pVars->Call(pVars, "GetCount", &count);
	printf("count is: %d\n", count);
	pCndVar->Call(pCndVar, "Broadcast", NULL);
	pMtx->Call(pMtx, "Unlock", NULL);

	printf("thread_condition_notifier finished.......\n");

	return 0;
}

static int thread_condition_waiter(void* aArg)
{
	(void)aArg;

	fflush(stdout);
	Mutex* pMtx = NULL;
	pVars->Call(pVars, "GetMutex", &pMtx);
	pMtx->Call(pMtx, "Lock", NULL);

	while (true)
	{
		int count = 0;
		pVars->Call(pVars, "GetCount", &count);
		if (count <= 0) break;
		printf("count is: %d\n", count);
		fflush(stdout);
		ConditionVariable* pCndVar = NULL;
		pVars->Call(pVars, "GetCondVar", &pCndVar);
		pCndVar->Call(pCndVar, "Wait", pMtx, NULL);

		printf("thread_condition_waiter waiting waiting waiting waiting waiting~~~~~~~~~~~~~~~~~~~~.\n");
	}
	pMtx->Call(pMtx, "Unlock", NULL);

	printf("out of loop, unlocked... ~~~~~~~~~~~~~~~~~~~~~~~~~\n");
	return 0;
}

static void test_condition_variables(void)
{
	__MEMS();

	Thread* pTh1 = __SNEW(Thread);
	Thread* pThs[40];

	/* Set the global counter to the number of threads to run. */
	pVars->Call(pVars, "SetCount", 40);
	printf("Count: 40.\n");

	/* Start the waiting thread (it will wait for gCount to reach
	   zero). */
	pTh1->Call(pTh1, "Create", thread_condition_waiter, NULL, NULL);
	printf("t1 created.\n");

	/* Start a bunch of child threads (these will decrease gCount by 1
	   when they finish) */
	for (int i = 0; i < 40; ++i)
	{
		pThs[i] = __SNEW(Thread);
	}
	for (int i = 0; i < 40; ++i)
	{
		pThs[i]->Call(pThs[i], "Create", thread_condition_notifier, NULL, NULL);
		printf("t%d created.\n", i);
	}

	/* Wait for the waiting thread to finish */
	pTh1->Call(pTh1, "Join", NULL, NULL);
	printf("t1 joined.\n");

	/* Wait for the other threads to finish */
	for (int i = 0; i < 40; ++i)
	{
		pThs[i]->Call(pThs[i], "Join", NULL, NULL);
		printf("t%d joined.\n", i);
	}

	__MEMT();
}

/* test_yield */
static int thread_yield(void* aArg)
{
	(void)aArg;

#undef Yield
	__STATIC(Thread)->Yield();
	return 0;
}

static void test_yield(void)
{
	__MEMS();

	Thread * pThs[40];

	for (int i = 0; i < 40; ++i)
	{
		pThs[i] = __SNEW(Thread);
		pThs[i]->Call(pThs[i], "Create", thread_yield, NULL, NULL);
	}
	
#undef Yield
	__STATIC(Thread)->Yield();

	/* Wait for the threads to finish */
	for (int i = 0; i < 40; ++i)
	{
		pThs[i]->Call(pThs[i], "Join", NULL, NULL);
	}

	__MEMT();
}

/* test_sleep */
#define NSECS_PER_SECOND 1000000000
static void timespec_add_nsec(struct timespec* ts, long tv_nsec)
{
	ts->tv_sec += tv_nsec / NSECS_PER_SECOND;
	ts->tv_nsec += tv_nsec % NSECS_PER_SECOND;
	if (ts->tv_nsec >= NSECS_PER_SECOND)
	{
		ts->tv_sec++;
		ts->tv_nsec -= NSECS_PER_SECOND;
	}
}

static int timespec_compare(struct timespec* a, struct timespec* b)
{
	if (a->tv_sec != b->tv_sec)
	{
		return a->tv_sec - b->tv_sec;
	}
	else if (a->tv_nsec != b->tv_nsec)
	{
		return a->tv_nsec - b->tv_nsec;
	}
	else
	{
		return 0;
	}
}

static void test_sleep(void)
{
	struct timespec ts;
	struct timespec interval;
	struct timespec end_ts;

	interval.tv_sec = 0;
	interval.tv_nsec = NSECS_PER_SECOND / 10;

	/* Calculate current time + 100ms */
	timespec_get(&ts, TIME_UTC);
	timespec_add_nsec(&ts, NSECS_PER_SECOND / 10);

	/* Sleep... */
	__STATIC(Thread)->Sleep(&interval, NULL);

	timespec_get(&end_ts, TIME_UTC);

	if (timespec_compare(&ts, &end_ts) <= 0)
	{
		printf("success to assert.\n");
	}
	else
	{
		printf("Failed to assert.\n");
	}
}

/* test_time */
static void test_time(void)
{
	struct timespec ts;
	timespec_get(&ts, TIME_UTC);
}

/* test_once */
static void thread_once_func(void)
{
	Mutex* pMtx = NULL;
	pVars->Call(pVars, "GetMutex", &pMtx);
	pMtx->Call(pMtx, "Lock", NULL);
	pVars->Call(pVars, "++");
	pMtx->Call(pMtx, "Unlock", NULL);
}

static int thread_once(void* data)
{
	int i;

	(void)data;

	OnceFlag* flags = NULL;
	pVars->Call(pVars, "OnceFlag", &flags);
	for (i = 0; i < 10000; i++)
	{
		__STATIC(OnceFlag)->CallOnce(&flags[i], thread_once_func);
	}

	return 0;
}

#define TEST_ONCE_N_THREADS 16
static void test_once(void)
{
	__MEMS();

	OnceFlag once_flag_init = ONCEFLAGINIT;
	Thread *pThreads[TEST_ONCE_N_THREADS];

	OnceFlag* flags = NULL;
	pVars->Call(pVars, "OnceFlag", &flags);
	/* Initialize 10000 once_flags */
	for (int i = 0; i < 10000; i++)
	{
		flags[i] = once_flag_init;
	}

	Mutex* pMtx = NULL;
	pVars->Call(pVars, "GetMutex", &pMtx);
	/* Clear the global counter. */
	pMtx->Call(pMtx, "Lock", NULL);
	pVars->Call(pVars, "SetCount", 0);
	pMtx->Call(pMtx, "Unlock", NULL);

	/* Create threads */
	for (int i = 0; i < TEST_ONCE_N_THREADS; i++)
	{
		pThreads[i] = __SNEW(Thread);
		pThreads[i]->Call(pThreads[i], "Create", thread_once, NULL, NULL);
	}

	/* Wait for all threads to finish executing. */
	for (int i = 0; i < TEST_ONCE_N_THREADS; i++)
	{
		pThreads[i]->Call(pThreads[i], "Join", NULL, NULL);
	}

	/* Check the global count */
	int count = 0;
	pVars->Call(pVars, "GetCount", &count);
	if (count == 10000)
	{
		printf("Success to assert..\n");
	}
	else
	{
		printf("Failed to assert..\n");
	}

	__MEMT();
}

/* test_tss */
struct TestThreadSpecificData {
	ThreadLocalStorage key;
	Mutex *mutex;
	int values_freed;
} test_tss_data;

static void test_tss_free(void* val)
{
    test_tss_data.mutex->Call(test_tss_data.mutex, "Lock", NULL);
	test_tss_data.values_freed++;
    test_tss_data.mutex->Call(test_tss_data.mutex, "Unlock", NULL);
	free(val);
}

static int test_tss_thread_func(void* data)
{
	int* value = (int*)malloc(sizeof(int));

	(void)data;

	*value = rand();

    if (__STATIC(ThreadLocalStorage)->Get(test_tss_data.key) == NULL)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }
    __STATIC(ThreadLocalStorage)->Set(test_tss_data.key, value);
    if (__STATIC(ThreadLocalStorage)->Get(test_tss_data.key) == value)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }

    __STATIC(ThreadLocalStorage)->Set(test_tss_data.key, NULL);
    if (__STATIC(ThreadLocalStorage)->Get(test_tss_data.key) == NULL)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }
    __STATIC(ThreadLocalStorage)->Set(test_tss_data.key, value);
    if (__STATIC(ThreadLocalStorage)->Get(test_tss_data.key) == value)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }

	return 0;
}

#define TEST_TSS_N_THREADS 256
static void test_tss(void)
{
    __MEMS();

	Thread *pThreads[TEST_TSS_N_THREADS];
	int* value = (int*)malloc(sizeof(int));

	*value = rand();

    __STATIC(ThreadLocalStorage)->Create(&test_tss_data.key, test_tss_free);
    test_tss_data.mutex = __SNEW(Mutex);
    test_tss_data.mutex->Call(test_tss_data.mutex, "Init", MutexType_PLAIN, NULL);
	test_tss_data.values_freed = 0;

	//assert(tss_get(test_tss_data.key) == NULL);
    if (__STATIC(ThreadLocalStorage)->Get(test_tss_data.key) == NULL)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }
    __STATIC(ThreadLocalStorage)->Set(test_tss_data.key, value);
	//assert(tss_get(test_tss_data.key) == value);
    if (__STATIC(ThreadLocalStorage)->Get(test_tss_data.key) == value)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }

	for (int i = 0; i < TEST_TSS_N_THREADS; i++)
	{
        pThreads[i] = __SNEW(Thread);
        pThreads[i]->Call(pThreads[i], "Create", test_tss_thread_func, NULL, NULL);
	}

	for (int i = 0; i < TEST_TSS_N_THREADS; i++)
	{
        pThreads[i]->Call(pThreads[i], "Join", NULL, NULL);
	}

	//assert(test_tss_data.values_freed == TEST_TSS_N_THREADS);
    if (test_tss_data.values_freed == TEST_TSS_N_THREADS)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }
	//assert(tss_get(test_tss_data.key) == value);
    if (__STATIC(ThreadLocalStorage)->Get(test_tss_data.key) == value)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }
    __STATIC(ThreadLocalStorage)->Delete(test_tss_data.key);
    if (__STATIC(ThreadLocalStorage)->Get(test_tss_data.key) == NULL)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }
    if (test_tss_data.values_freed == TEST_TSS_N_THREADS)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }

	free(value);

    __MEMT();
}

/* test_mutex_timed */
struct TestMutexTimedData {
	Mutex * mutex;
	struct timespec start;
	struct timespec timeout;
	struct timespec end;
	struct timespec upper;
};

static int test_mutex_timed_thread_func(void* arg)
{
	struct timespec ts;
	struct TestMutexTimedData* data = (struct TestMutexTimedData*) arg;

    ConcurrenceReturnValue ret = ConcurrenceReturnValue_NON;
    data->mutex->Call(data->mutex, "TimedLock", &data->timeout, &ret);
    if (ret == ConcurrenceReturnValue_TIMEDOUT)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }

	timespec_get(&ts, TIME_UTC);
    if (timespec_compare(&ts, &(data->start)) >= 0)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }
    if (timespec_compare(&ts, &(data->timeout)) >= 0)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }
    if (timespec_compare(&ts, &(data->end)) >= 0)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }

    data->mutex->Call(data->mutex, "Lock", &ret);
    if (ret == ConcurrenceReturnValue_SUCCESS)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }

	timespec_get(&ts, TIME_UTC);
    if (timespec_compare(&ts, &(data->end)) >= 0)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }
    if (timespec_compare(&ts, &(data->upper)) < 0)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }

    data->mutex->Call(data->mutex, "Unlock", NULL);

	return 0;
}

static void test_mutex_timed(void)
{
    __MEMS();

	struct TestMutexTimedData data;
	Thread *pThread;
	struct timespec interval = { 0, };
	struct timespec start;
	struct timespec end;

	interval.tv_sec = 0;
	interval.tv_nsec = (NSECS_PER_SECOND / 10) * 2;

    data.mutex = __SNEW(Mutex);
    data.mutex->Call(data.mutex, "Init", MutexType_TIMED, NULL);
    data.mutex->Call(data.mutex, "Lock", NULL);

	timespec_get(&(data.start), TIME_UTC);
	data.timeout = data.start;
	timespec_add_nsec(&(data.timeout), NSECS_PER_SECOND / 10);
	data.end = data.timeout;
	timespec_add_nsec(&(data.end), NSECS_PER_SECOND / 10);
	data.upper = data.end;
	timespec_add_nsec(&(data.upper), NSECS_PER_SECOND / 10);

    pThread = __SNEW(Thread);
    pThread->Call(pThread, "Create", test_mutex_timed_thread_func, &data, NULL);

	timespec_get(&start, TIME_UTC);
    if (__STATIC(Thread)->Sleep(&interval, &interval) == 0)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }
	timespec_get(&end, TIME_UTC);
    data.mutex->Call(data.mutex, "Unlock", NULL);

    pThread->Call(pThread, "Join", NULL, NULL);

    __MEMT();
}

/* test_thrd_exit */
static int test_thrd_exit_func(void* arg)
{
	test_sleep();
    __STATIC(Thread)->Exit(2);
	return 1;
}

static void test_thrd_exit(void)
{
    __MEMS();

    Thread *pThread = __SNEW(Thread);
	int res;
    pThread->Call(pThread, "Create", test_thrd_exit_func, NULL, NULL);
    pThread->Call(pThread, "Join", &res, NULL);
	//assert(thrd_join(thread, &res));
    if (res == 2)
    {
        printf("Success to assert.\n");
    }
    else
    {
        printf("Failed to assert.\n");
    }

    __MEMT();
}

/////////////////////////////////////////////////////////////

void test_run(const Test* test, unsigned int seed)
{
	fputs("  ", stdout);
	fputs(test->Name, stdout);
	for (size_t i = strlen(test->Name); i < 48; i++)
	{
		fputc(' ', stdout);
	}
	fflush(stdout);
	srand(seed);
	test->func();
	fprintf(stdout, "OK\n");
}

void tests_run(const Test* tests)
{
	unsigned int seed;
	struct timespec tv;

	timespec_get(&tv, TIME_UTC);
	srand(tv.tv_nsec);
	seed = rand();

	for (int test_n = 0; tests[test_n].Name; test_n++)
	{
		test_run(&tests[test_n], seed);
	}
}

const Test unit_tests[] =
{
	{"first", test_first},
	{ "thread-arg-and-retval", test_thread_arg_and_retval },
	{ "thread-local-storage", test_thread_local_storage },
	{ "mutex-locking", test_mutex_locking },
	{ "mutex-recursive", test_mutex_recursive },
	{ "condition-variables", test_condition_variables },
	{ "yield", test_yield },
	{ "sleep", test_sleep },
	{ "time", test_time },
	{ "once", test_once },
	{ "thread-specific-storage", test_tss },
	{ "mutex-timed", test_mutex_timed },
	{ "thread-exit", test_thrd_exit },
	{ NULL, }
};

int main(int argc, char** argv)
{
	__MEMS();

	pVars = __SNEW(GlobalVars);
	pVars->Call(pVars, "InitMutex", MutexType_PLAIN, NULL);
	pVars->Call(pVars, "InitCondVar", NULL);

    tests_run(unit_tests);

	__MEMT();
	return 0;
}
